/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 *
 */

import { hvigor, hvigorCore, HvigorCoreNode, Module, PathUtil, Project } from '@ohos/hvigor';
import { getAlignTarget } from '@ohos/hvigor';
import fs from 'fs';
import path from 'path';

import { moduleOhosConfigManager } from '../../common/global/module-ohos-config-manager.js';
import { CommonConst } from '../../const/common-const.js';
import { ProjectModel } from '../../model/project/project-model.js';
import { HspOhosConfig } from '../../options/build/hap-ohos-config.js';
import { GlobalProjectDataService } from '../../tasks/service/global-project-data-service.js';
import { OhosModuleTask } from '../../tasks/task/ohos-module-task.js';
import { TaskCreatorManager } from '../../tasks/task-creator.js';
import { legacyHapTasksInitialize, legacyHarTasksInitialize } from '../../utils/legacy-task-initializer.js';
import { configFileLoader } from '../../utils/loader/config-file-loader.js';
import { ohPackageLoader } from '../../utils/loader/file/oh-package-loader';
import { resModelLoader } from '../../utils/loader/file/res-model-loader.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { OneSdkValidator } from '../../utils/one-sdk-validator.js';
import { ProjectTaskInitializer } from '../../utils/project-task-initializer.js';
import { ResolverCache } from '../../utils/resolver/resolver-cache.js';
import { RunCommandUtil } from '../../utils/run-command-util.js';
import { TaskInitializer } from '../../utils/task-initializer.js';
import { AppPlugin } from '../app-plugin.js';
import { AbstractHapModulePlugin } from '../common/abstract-hap-module-plugin.js';
import { AbstractHarModulePlugin } from '../common/abstract-har-module-plugin.js';
import { AbstractProjectPlugin } from '../common/abstract-project-plugin.js';
import { HspPlugin } from '../hsp-plugin.js';
import { LegacyAppPlugin } from '../legacy/legacy-app-plugin.js';

import { LegacyPluginBuilder } from './legacy-plugin-builder.js';
import { PluginBuilder } from './plugin-builder.js';
import { PluginInspector } from './plugin-inspector.js';
import { HVIGOR_PROJECT_ROOT_DIR } from '@ohos/hvigor/src/common/const/path-const';
import fse from 'fs-extra';
import { getJson5Obj } from '../../utils/json-util.js';
import { INCREMENTAL_OPTIMIZATION } from '@ohos/hvigor/src/common/const/const.js';
import { CompileDependencyCalculator } from '../performance/task-schedule-optimization.js';

/**
 * 创建不同模型Plugin的简单工厂类
 *
 * @since 2022/2/23
 */

const _log: OhosLogger = OhosLogger.getLogger('PluginFactory');
export class PluginFactory {
  public static getAppPlugin(project: Project, isFaMode = false): AppPlugin {
    // 清除configFileLoader的缓存，避免构建使用了上次的daemon缓存数据
    configFileLoader.clean();
    resModelLoader.clean();
    ohPackageLoader.clean();

    const appPlugin: AbstractProjectPlugin = isFaMode ? new LegacyAppPlugin(project) : new AppPlugin(project);
    appPlugin.doProjectInspection();

    // 模块加载完成第一个钩子函数, 来校验API
    project.afterBindSystemPlugins(async () => {
      appPlugin.initGlobalData();

      appPlugin.initBuildOptionMap(appPlugin._project.getBuildProfilePath());

      appPlugin.initTaskService();

      await appPlugin.getTaskService()?.setup();

      appPlugin.initContext();

      ProjectTaskInitializer.initialize(appPlugin, isFaMode);
    });

    if (hvigorCore.getParameter().getProperty(INCREMENTAL_OPTIMIZATION) === true) {
      hvigor.taskGraphResolved(() => {
        const calculator = new CompileDependencyCalculator(project);
        calculator.computeAndSetCompileDependencies();
      });
    }

    // 如果修改了oh-package.json文件 落盘至build文件夹
    hvigorCore.hvigorNodeBeforeEvaluatedInternalHook(() => {
      const targetName = getAlignTarget() ?? '';
      if (!this.isOnlyCleanTask()) {
        const ohPackageJsonPath = path.resolve(HVIGOR_PROJECT_ROOT_DIR, CommonConst.OH_PACKAGE_JSON5);
        let newParameterFile;
        if (fse.existsSync(ohPackageJsonPath)) {
          const ohPackageJsonObj = getJson5Obj(ohPackageJsonPath);
          const configParameterFile = hvigorCore.getParameter().getExtParam(CommonConst.PARAMETER_FILE);
          newParameterFile = configParameterFile ?? ohPackageJsonObj?.parameterFile;
        }
        ohPackageLoader.setIsChangeParameterFile(targetName, newParameterFile);
        ohPackageLoader.loadUpdatedOhPackageToDisk(targetName, newParameterFile);
        if (ohPackageLoader.shouldDoOhpmInstall()) {
          // 检查ohpm环境变量，如果不存在 报warn, 存在执行ohpm install依赖
          _log.info('start to execute ohpm install');
          // 支持一体化ohpm以及环境变量中配置的ohpm
          const ohpmBinPath = process.env['ohpmBin'] || 'ohpm';
          if ((ohpmBinPath && fs.existsSync(ohpmBinPath)) || RunCommandUtil.run('ohpm -v', undefined, true)) {
            const pathOfDependencyMap = path.resolve(PathUtil.getHvigorCacheDir(), CommonConst.DEPENDENCYMAP, targetName);
            _log.debug(`ohpm install command is "${ohpmBinPath}" i --all --target_path ${pathOfDependencyMap}`);
            let ohpmCommand;
            if (newParameterFile) {
              ohpmCommand = `"${ohpmBinPath}" i --all --target_path ${pathOfDependencyMap} --parameterFile=${newParameterFile}`;
            } else {
              ohpmCommand = `"${ohpmBinPath}" i --all --target_path ${pathOfDependencyMap}`;
            }
            const ohpmRes = RunCommandUtil.run(ohpmCommand, undefined, true);

            // 如果ohpm命令执行失败则报错
            if (!ohpmRes) {
              _log.printErrorExit('OHPM_INSTALL_FAILED');
            }
          } else {
            _log.warn('need to configure ohpm environment in your system, ' + 'please see official guide to configure ohpm environment');
          }
        }
      }

      // 此处计算工程ohpkjJson的hash值，从而让增量判断是否执行此任务, 避免重复计算，将计算过程下沉到此处
      const OhPackageJsonHash = GlobalProjectDataService.getInstance().initProjectPkgJsonFileHash(appPlugin.getProjectModel() as ProjectModel);
      GlobalProjectDataService.getInstance().setProjectPkgJsonFileHash(OhPackageJsonHash);
    });

    hvigorCore.hvigorNodeEvaluatedInternalHook(() => {
      this.ifThrowBuildModeError();
      ResolverCache.clear();
      appPlugin.getTaskService()?.initDependencyInfo();
      this.initializeTaskDepends(project, undefined, false);
      OneSdkValidator.apiInspection(appPlugin.getProjectModel()!);
      appPlugin.initTraceData();
      appPlugin.getTaskService()?.initProductData();
    });
    return <AppPlugin>appPlugin;
  }

  public static getHapPlugin(module: Module, isFaMode = false): AbstractHapModulePlugin {
    let hapPlugin: AbstractHapModulePlugin;
    let pluginBuilder;
    let cangjieObj;

    if (isFaMode) {
      pluginBuilder = new LegacyPluginBuilder(module);
      hapPlugin = pluginBuilder.createFaHapPlugin();
    } else {
      cangjieObj = this.dealCangjieSchema();
      pluginBuilder = new PluginBuilder(module);
      hapPlugin = pluginBuilder.createStageHapPlugin();
    }
    hapPlugin.initGlobalData();
    pluginBuilder.setUpModulePlugin(hapPlugin);
    let creationManager: TaskCreatorManager;
    if (isFaMode) {
      legacyHapTasksInitialize(hapPlugin);
    } else {
      creationManager = TaskInitializer.initializeHap(hapPlugin);
      try {
        cangjieObj?.registerCangjieHapTask(module, hapPlugin);
      } catch (err) {
        _log.debug(`failed to register the cangjie hap task: ${err}`);
      }
    }

    hvigorCore.hvigorAfterNodeInternalHook(module.getName(), async () => {
      hapPlugin.initContext();
    });

    hvigorCore.hvigorNodeEvaluatedInternalHook(() => {
      this.ifThrowBuildModeError();
      hapPlugin.getTaskService()?.initDependencyInfo();
      this.initializeTaskDepends(module, creationManager, isFaMode);

      PluginInspector.checkFeatureEntryModules(hapPlugin);
    });
    return hapPlugin;
  }

  public static getHarPlugin(module: Module, isFaMode = false): AbstractHarModulePlugin {
    let harPlugin: AbstractHarModulePlugin;
    let pluginBuilder;
    let creationManager: TaskCreatorManager;
    let cangjieObj;

    if (isFaMode) {
      pluginBuilder = new LegacyPluginBuilder(module);
      harPlugin = pluginBuilder.createFaHarPlugin();
    } else {
      cangjieObj = this.dealCangjieSchema();
      pluginBuilder = new PluginBuilder(module);
      harPlugin = pluginBuilder.createStageHarPlugin();
    }
    harPlugin.initGlobalData();
    pluginBuilder.setUpModulePlugin(harPlugin);
    if (isFaMode) {
      legacyHarTasksInitialize(harPlugin);
    } else {
      creationManager = TaskInitializer.initializeHar(harPlugin);
      try {
        cangjieObj?.registerCangjieHarTask(module, harPlugin);
      } catch(err) {
        _log.debug(`failed to register the cangjie har task: ${err}`);
      }
    }

    hvigorCore.hvigorAfterNodeInternalHook(module.getName(), async () => {
      harPlugin.initContext();
    });

    hvigorCore.hvigorNodeEvaluatedInternalHook(() => {
      this.ifThrowBuildModeError();
      harPlugin.getTaskService()?.initDependencyInfo();
      PluginInspector.checkAtomicServiceHspModules(harPlugin);
      this.initializeTaskDepends(module, creationManager, isFaMode);
    });
    return harPlugin;
  }

  public static getHspPlugin(module: Module): HspPlugin {
    const pluginBuilder = new PluginBuilder(module);
    const hspPlugin = pluginBuilder.createStageHspPlugin();
    pluginBuilder.setUpModulePlugin(hspPlugin);
    const creatorManager: TaskCreatorManager = TaskInitializer.initializeHsp(hspPlugin);
    this.initHspPluginGlobalData(module);
    hvigorCore.hvigorAfterNodeInternalHook(module.getName(), async () => {
      hspPlugin.initContext();
    });

    hvigorCore.hvigorNodeEvaluatedInternalHook(() => {
      this.ifThrowBuildModeError();
      hspPlugin.getTaskService()?.initDependencyInfo();
      creatorManager.configure();
    });
    return hspPlugin;
  }

  private static initializeTaskDepends(module: HvigorCoreNode, creationManager?: TaskCreatorManager, isFa?: boolean) {
    if (!isFa && creationManager) {
      creationManager?.configure();
      return;
    }
    module
      .getTaskContainer()
      .getAllTasks()
      .filter((task) => task instanceof OhosModuleTask)
      .forEach((task) => (task as OhosModuleTask).initTaskDepends());
  }

  private static isOnlyCleanTask() {
    const taskList = hvigor.getCommandEntryTask();
    return taskList?.length === 1 && taskList[0] === 'clean';
  }

  private static initHspPluginGlobalData(module: Module) {
    const ohosConfig = module.getConfigOpt()?.getObject('ohos') as HspOhosConfig;

    if (!ohosConfig) {
      return;
    }

    moduleOhosConfigManager.loaderConfig(module.getName(), ohosConfig);
  }

  /**
   * 通过公共参数判断是否要对未存在的buildModeInfo报错
   *
   * @private
   */
  private static ifThrowBuildModeError() {
    const buildModeInfo = GlobalProjectDataService.getInstance().getBuildModeInfo();
    const buildModeName = buildModeInfo.buildModeName;
    const buildProfilePath = buildModeInfo.buildProfilePath;
    if (buildModeInfo.ifThrowError) {
      _log.printErrorExit('BUILD_MODE_NOT_DEFINED', [buildModeName, buildProfilePath], [[buildModeName, buildProfilePath]]);
    }
  }

  /**
   * 加载仓颉，动态处理schema
   *
   * @private
   */
  private static dealCangjieSchema(): any {
    let cangjieObj;
    try {
      cangjieObj = require('@ohos/cangjie-build-support/index');
      cangjieObj?.handleCangjieSchema();
    } catch (err: any) {
      _log.debug(`failed to load cangjie to dynamically generate schemas: ${err}`);
    }
    return cangjieObj;
  }
}
